package main

import (
	"fmt"
	"math"
)

// 函数也是值。它们可以像其他值一样传递。
// 函数值可以用作函数的参数或返回值。
func main() {
	FunVar()   //函数值
	FunClose() //函数闭包
	fibonaci() //斐波那契数列
}

func FunVar() {
	// 函数变量
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}

	fmt.Println(hypot(5, 12))      // hypot(5,12)
	fmt.Println(compute(hypot))    // hypot(3,4)
	fmt.Println(compute(math.Pow)) // Pow(3,4)
}

// 给传进来的函数参数放入 (3,4) 后执行
func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

// -----------------------------------------------------------------------------------\\

// Go 函数可以是一个闭包。闭包是一个函数值，它引用了其函数体之外的变量。 该函数可以访问并赋予其引用的变量值，换句话说，该函数被“绑定”到了这些变量
func FunClose() {
	pos, neg := addr(), addr()
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(-2*i),
		)
	}
}

// 函数 adder 返回一个闭包。每个闭包都被绑定在其各自的 sum 变量上。
func addr() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

// ===================================================================================================
// 用函数闭包实现斐波那契数列
func fibonaci() {
	fn := fibonacci()
	for i := 0; i < 10; i++ {
		fmt.Println(fn())
	}
}

// 斐波纳契闭包
func fibonacci() func() int {
	a1, a2 := 0, 1
	return func() int {
		res := a1
		a1, a2 = a2, a1+a2
		return res
	}
}
